# -*- coding: utf-8 -*-
import io
import textwrap
import string
import pytest
import decimal
import canmatrix.formats.dbc


from canmatrix.CanMatrix import CanMatrix
from canmatrix.Signal import Signal
from canmatrix.Frame import Frame
from canmatrix.Ecu import Ecu
from canmatrix.Define import Define

def test_long_signal_name_imports():
    long_signal_name = u'FAILURE_ZELL_UNTERTEMPERATUR_ENTLADEN_ALARM_IDX_01'
    assert len(long_signal_name) > 32
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BO_ 1 testFrame1: 1 TEST_ECU
     SG_ someShortenedDummyName: 1|2@0+ (1,0) [0|0] ""  CCL_TEST

    BA_ "SystemSignalLongSymbol" SG_ 1 someShortenedDummyName "{}";  
    ''').format(long_signal_name).encode('utf-8'))

    matrix = canmatrix.formats.dbc.load(dbc)

    assert matrix.frames[0].signals[0].name == long_signal_name
    outdbc = io.BytesIO()
    canmatrix.formats.dump(matrix, outdbc, "dbc")
    long_name_found = False
    name_found = False

    for line in outdbc.getvalue().decode('utf8').split('\n'):
        if line.strip().startswith("SG_"):
            assert len(line.split()[1]) <= 32
            name_found = True
        if line.strip().startswith("BA_ "):
            assert line.split()[5][1:-2] == long_signal_name
            long_name_found = True
    assert long_name_found is True
    assert name_found is True


def test_create_define():
    defaults = {}
    test_string = canmatrix.formats.dbc.create_define("my_data_type", Define('ENUM "A","B"'), "BA_", defaults)
    assert test_string == 'BA_DEF_ BA_ "my_data_type" ENUM "A","B";\n'


def test_create_attribute_string():
    test_string = canmatrix.formats.dbc.create_attribute_string("my_attribute", "BO_", "name", "value", True)
    assert test_string == 'BA_ "my_attribute" BO_ name "value";\n'
    test_string = canmatrix.formats.dbc.create_attribute_string("my_attribute", "BO_", "name", 1.23, False)
    assert test_string == 'BA_ "my_attribute" BO_ name 1.23;\n'


def test_create_comment_string():
    test_string = canmatrix.formats.dbc.create_comment_string("BO_", "ident", "some comment", "utf8", "utf8", "")
    assert test_string == b'CM_ BO_ ident "some comment";\n'


def test_parse_comment_from_dbc():
    dbc = io.BytesIO(textwrap.dedent(u'''\
        BO_ 1 someFrame: 1 someEcu
         SG_ someSignal: 1|2@0+ (1,0) [0|0] ""  CCL_TEST

        CM_ SG_ 1 someSignal "resistance setting (0-100%)" ; 
        ''').encode('utf-8'))

    matrix = canmatrix.formats.dbc.load(dbc)
    assert matrix.frames[0].signals[0].comment == "resistance setting (0-100%)"


def test_parse_multi_line_comment():
    dbc = io.BytesIO(textwrap.dedent(u'''\
        BO_ 1 someFrame: 1 someEcu
         SG_ someSignal: 1|2@0+ (1,0) [0|0] ""  CCL_TEST

        CM_ SG_ 1 someSignal "Debug request message from the ECU to the BMS.
** ignore for now, more definition to be provided in Rev 14 regarding which messages to change if we have this debug flag implemented. " ;
        ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc)
    assert matrix.frames[0].signals[0].comment == 'Debug request message from the ECU to the BMS.\n** ignore for now, more definition to be provided in Rev 14 regarding which messages to change if we have this debug flag implemented. '


def test_long_frame_name_imports():
    long_frame_name = u'A_VERY_LONG_FRAME_NAME_WHICH_SHOULD_BE_SPLIT_SOMEHOW'
    assert len(long_frame_name) > 32
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BO_ 1 shortendeFrameName: 1 someEcu
     SG_ someSignal: 1|2@0+ (1,0) [0|0] ""  CCL_TEST

    BA_ "SystemMessageLongSymbol" BO_ 1 "{}";  
    ''').format(long_frame_name).encode('utf-8'))

    matrix = canmatrix.formats.dbc.load(dbc)
    long_name_found = False
    name_found = False

    assert matrix.frames[0].name == long_frame_name
    outdbc = io.BytesIO()
    canmatrix.formats.dump(matrix, outdbc, "dbc")
    for line in outdbc.getvalue().decode('utf8').split('\n'):
        if line.strip().startswith("BO_"):
            assert len(line.split()[2][:-1]) <= 32
            name_found = True
        if line.strip().startswith("BA_ "):
            assert line.split()[4][1:-2] == long_frame_name
            long_name_found = True
    assert long_name_found is True
    assert name_found is True


def test_long_ecu_name_imports():
    long_ecu_name = u'A_VERY_LONG_ECU_NAME_WHICH_SHOULD_BE_SPLIT_SOMEHOW'
    assert len(long_ecu_name) > 32
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BU_: SoMEShortenedEcuName
    BO_ 1 testFrame1: 1 SoMEShortenedEcuName
     SG_ someSignal: 1|2@0+ (1,0) [0|0] ""  CCL_TEST

    BA_ "SystemNodeLongSymbol" BU_ SoMEShortenedEcuName "{}";  
    ''').format(long_ecu_name).encode('utf-8'))

    matrix = canmatrix.formats.dbc.load(dbc)
    long_name_found = False
    name_found = False

    assert matrix.ecus[0].name == long_ecu_name
    outdbc = io.BytesIO()
    canmatrix.formats.dump(matrix, outdbc, "dbc")
    for line in outdbc.getvalue().decode('utf8').split('\n'):
        if line.strip().startswith("BU_"):
            assert len(line.split()[1]) <= 32
            name_found = True
        if line.strip().startswith("BA_ "):
            assert line.split()[4][1:-2] == long_ecu_name
            long_name_found = True
    assert long_name_found is True
    assert name_found is True


def test_long_envvar_name_imports():
    long_envvar_name = u'A_VERY_LONG_ENVIROMENT_NAME_WHICH_SHOULD_BE_SPLIT_SOMEHOW'
    assert len(long_envvar_name) > 32
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BO_ 1 frameName: 1 someEcu
     SG_ someSignal: 1|2@0+ (1,0) [0|0] ""  CCL_TEST

    EV_ someShortendEnvVar: 0 [0|0] "" 0 2 DUMMY_NODE_VECTOR0 Vector__XXX;
    
    BA_ "SystemEnvVarLongSymbol" EV_ someShortendEnvVar "{}";  
    ''').format(long_envvar_name).encode('utf-8'))

    matrix = canmatrix.formats.dbc.load(dbc)

    assert list(matrix.env_vars)[0] == long_envvar_name
    outdbc = io.BytesIO()
    canmatrix.formats.dump(matrix, outdbc, "dbc")
    long_name_found = False
    name_found = False

    for line in outdbc.getvalue().decode('utf8').split('\n'):
        if line.strip().startswith("EV_"):
            assert len(line.split()[1]) <= 32
            name_found = True
        if line.strip().startswith("BA_ "):
            assert line.split()[3][1:-2] == long_envvar_name
            long_name_found = True
    assert long_name_found is True
    assert name_found is True


def test_enum_with_comma():
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BA_DEF_ "example0" ENUM "Val1",",";
    BA_DEF_ BO_ "example1" ENUM "Val 1","vector_leerstring",""," ","'","(",")","[","]","/","-","|","{","}",";",":","<",">",".","?","!","@","#","$","%","^","&","=","`","~";
    BA_DEF_ SG_ "example2" ENUM "Val1",",";
    BA_DEF_ EV_ "example3" ENUM "Val1",",";
    BA_DEF_ BU_ "example4" ENUM "Val1",",";
    BA_DEF_DEF_ "example0" ",";
    BA_DEF_DEF_ "example1" ",";
    BA_DEF_DEF_ "example2" ",";
    BA_DEF_DEF_ "example3" ",";
    BA_DEF_DEF_ "example4" ",";
    ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")

    assert matrix.frame_defines[u'example1'].values == ["Val 1", "", ""] + list(" '()[]/-|{};:<>.?!@#$%^&=`~")
    assert matrix.signal_defines[u'example2'].values == ['Val1', ',']
    assert matrix.ecu_defines[u'example4'].values == ['Val1', ',']


@pytest.mark.parametrize(
    'character',
    [
        ['{}'.format(c if c != '"' else '\\"')]
        for c in string.punctuation
    ],
)
def test_enum_with_special_character(character):
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BA_DEF_ BO_ "example1" ENUM "Val 1","{}";
    ''').format(character[0]).encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    assert matrix.frame_defines[u'example1'].values == ["Val 1", character[0]]


def test_export_of_unknown_defines():
    db = CanMatrix()

    db.add_frame_defines("Receivable", 'BOOL False True')
    db.add_frame_defines("Sendable", 'BOOL False True')
    for (dataType, define) in db.frame_defines.items():
        orig_definition = define.definition
        canmatrix.formats.dbc.check_define(define)
        assert orig_definition != define.definition

    db.add_signal_defines("LongName", 'STR')
    for (dataType, define) in db.signal_defines.items():
        orig_definition = define.definition
        canmatrix.formats.dbc.check_define(define)
        assert orig_definition != define.definition
    frame = Frame("someFrame")
    signal = Signal("SomeSignal")
    signal.add_attribute("LongName", "EnableCalcIDCTrip Calc. IDC trip")
    frame.add_signal(signal)
    db.add_frame(frame)

    db.add_ecu_defines("someName", 'STRING')
    for (dataType, define) in db.ecu_defines.items():
        orig_definition = define.definition
        canmatrix.formats.dbc.check_define(define)
        assert orig_definition == define.definition

    db.add_global_defines("someGlobaName", 'BOOL')
    for (dataType, define) in db.global_defines.items():
        orig_definition = define.definition
        canmatrix.formats.dbc.check_define(define)
        assert orig_definition != define.definition

    outdbc = io.BytesIO()
    canmatrix.formats.dump(db, outdbc, "dbc")
    for line in outdbc.getvalue().decode('utf8').split('\n'):
        if line.startswith("BA_DEF_ "):
            assert line.endswith("STRING;")
        if line.startswith("BA_ "):
            assert line.endswith('";')


def test_braces_in_attributes():
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BO_ 20 frameName: 1 someEcu
    SG_ sometext: 1|2@0+ (1,0) [0|0] ""  someOtherEcu
    
    BA_ "Signal Age [ms]" SG_ 20 sometext 5000;
     ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")


def test_defines_with_spaces():
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BU_: someOtherEcu

    BO_ 123 someFrame: 1 someOtherEcu

    EV_ someEnvVar: 0 [0|0] "" 0 2 DUMMY_NODE_VECTOR0 Vector__XXX;

    BA_DEF_ BU_ "Node Address" INT 0 255;
    BA_DEF_ BO_ "Period [ms]" INT 0 5000;
    BA_DEF_ BU_ "Description X" STRING;
    BA_DEF_ EV_ "some attrib" STRING;
    BA_ "Node Address" BU_ someOtherEcu 42;
    BA_ "Description X" BU_ someOtherEcu "Some Some Text";
    BA_ "Period [ms]" BO_ 123 3000;
    BA_ "some attrib" EV_ someEnvVar "some space";
     ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    assert matrix.ecu_defines["Node Address"].type == "INT"
    assert matrix.ecu_defines["Node Address"].min == 0
    assert matrix.ecu_defines["Node Address"].max == 255
    assert matrix.frame_defines["Period [ms]"].min == 0
    assert matrix.frame_defines["Period [ms]"].max == 5000
    assert matrix.frames[0].attributes["Period [ms]"] == '3000'
    assert matrix.env_vars["someEnvVar"]["attributes"]["some attrib"] == '"some space"'
    assert matrix.ecus[0].attributes["Description X"] == "Some Some Text"


def test_writing_complex_multiplex():
    db = CanMatrix()
    frame = Frame("someFrame")
    frame.is_complex_multiplexed = True
    signal = Signal("mx")
    signal.mux_val_max = 5
    signal.mux_val_min = 1
    signal.muxer_for_signal = 4
    frame.add_signal(signal)
    db.add_frame(frame)
    outdbc = io.BytesIO()
    canmatrix.formats.dump(db, outdbc, "dbc")
    for line in outdbc.getvalue().decode('utf8').split('\n'):
        if "SG_MUL_VAL" in line:
            return 
    assert False


def test_defines_with_special_cars():
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BU_: someOtherEcu

    BO_ 123 someFrame: 1 someOtherEcu
     SG_ someSignal: 1|2@0+ (1,0) [0|0] ""  CCL_TEST

    BA_DEF_ SG_ "Accuracy" STRING;
    BA_ "Accuracy" SG_ 123 someSignal "+/- 10.2 at 55.1%";
     ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    assert matrix.frames[0].signals[0].attributes["Accuracy"] == "+/- 10.2 at 55.1%"

#@pytest.mark.skip(reason="J1939 functionality is only partially implemented and currently breaks test chain")
def test_j1939_frametype():
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BU_: someOtherEcu

    BO_ 2147483648 someFrame: 1 someOtherEcu
     SG_ someSignal: 1|2@0+ (1,0) [0|0] ""  CCL_TEST
    
    BA_DEF_ BO_ "VFrameFormat" ENUM "StandardCAN","ExtendedCAN","J1939PG";
    BA_ "VFrameFormat" BO_ 2147483648 2;
     ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    assert matrix.frames[0].is_j1939 == True

    # negative test
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BU_: someOtherEcu

    BO_ 2147483648 someFrame: 1 someOtherEcu
     SG_ someSignal: 1|2@0+ (1,0) [0|0] ""  CCL_TEST

    BA_DEF_ BO_ "VFrameFormat" ENUM "StandardCAN","ExtendedCAN","J1939PG";
    BA_ "VFrameFormat" BO_ 2147483648 0;
     ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    assert matrix.frames[0].is_j1939 == False

def test_multiplex_frame():
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BU_: someOtherEcu
    
    BO_ 123 someFrame: 8 someOtherEcu
     SG_ someSignal m2 : 8|8@1+ (1,0) [0|9] ""  CCL_TEST
     SG_ someOtherSignal m1 : 8|8@0+ (1,0) [0|9] ""  CCL_TEST
     SG_ someMultiplexor M : 0|8@1+ (1,0) [0|2] ""  CCL_TEST
    ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    assert matrix.frames[0].is_multiplexed

    assert matrix.frames[0].signal_by_name("someSignal").muxer_for_signal == "someMultiplexor"


def test_attributes_with_spaces_before_semicolumn():
    dbc = io.BytesIO(textwrap.dedent(u'''\
    BO_ 8 Frame_1: 8 Vector__XXX
    BO_ 9 Frame_2: 8 Vector__XXX
    BA_DEF_ BO_ "someAttribute" STRING ;
    BA_ "someAttribute" BO_ 8 "str" ;
    BA_DEF_DEF_ "someAttribute" "asd" ;
    ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    assert matrix.frames[0].attributes["someAttribute"] == 'str'
    assert matrix.frames[1].attribute("someAttribute", matrix) == 'asd'


def test_cycle_time_handling():
    dbc = io.BytesIO(textwrap.dedent(u'''\
        BO_ 17 Frame_1: 8 Vector__XXX
        SG_ sig2 : 8|8@1- (1,0) [0|0] "" Vector__XXX
        SG_ sig1 : 0|8@1- (1,0) [0|0] "" Vector__XXX
        
        
        BA_DEF_ BO_  "GenMsgCycleTime" INT 0 3600000;
        BA_DEF_ SG_  "GenSigCycleTime" INT 0 3600000;
        BA_DEF_DEF_  "GenMsgCycleTime" 0;
        BA_DEF_DEF_  "GenSigCycleTime" 0;
        BA_ "GenMsgCycleTime" BO_ 17 100;
        BA_ "GenSigCycleTime" SG_ 17 sig2 20;
        BA_ "GenSigCycleTime" SG_ 17 sig1 10;
    ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")

    assert matrix.frames[0].cycle_time == 100
    assert matrix.frames[0].signal_by_name("sig1").cycle_time == 10
    assert matrix.frames[0].signal_by_name("sig2").cycle_time == 20

#    assert "GenMsgCycleTime" not in matrix.frame_defines
#    assert "GenSigCycleTime" not in matrix.signal_defines

    outdbc = io.BytesIO()
    canmatrix.formats.dump(matrix, outdbc, "dbc")

    assert 'BA_ "GenMsgCycleTime" BO_ 17 100;' in outdbc.getvalue().decode('utf8')
    assert 'BA_ "GenSigCycleTime" SG_ 17 sig2 20;' in outdbc.getvalue().decode('utf8')
    assert 'BA_ "GenSigCycleTime" SG_ 17 sig1 10;' in outdbc.getvalue().decode('utf8')

    outdbc = io.BytesIO()
    canmatrix.formats.dump({"aa":matrix}, outdbc, "kcd")


def test_keep_cycle_time_defines():
    dbc = io.BytesIO(textwrap.dedent(u'''\
        BO_ 17 Frame_1: 8 Vector__XXX
        SG_ sig1 : 0|8@1- (1,0) [0|0] "" Vector__XXX
        
        BA_DEF_ BO_ "GenMsgCycleTime" INT 0 50000 ;
        BA_DEF_DEF_ "GenMsgCycleTime" 0 ;
    ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")

    outdbc = io.BytesIO()
    canmatrix.formats.dump(matrix, outdbc, "dbc")
    assert 'BA_DEF_ BO_ "GenMsgCycleTime" INT 0 50000' in outdbc.getvalue().decode('utf8')
    assert 'BA_DEF_DEF_ "GenMsgCycleTime" 0' in outdbc.getvalue().decode('utf8')


def test_unique_signal_names():
    db = CanMatrix()
    frame = Frame("some Frame")
    frame.add_signal(Signal("signal_name", size=1, start_bit=1))
    frame.add_signal(Signal("signal_name", size=2, start_bit=9))
    db.add_frame(frame)
    outdbc = io.BytesIO()
    canmatrix.formats.dump(db, outdbc, "dbc")
    assert "signal_name0" in outdbc.getvalue().decode('utf8')
    assert "signal_name1" in outdbc.getvalue().decode('utf8')

    outdbc = io.BytesIO()
    canmatrix.formats.dump(db, outdbc, "dbc", dbcUniqueSignalNames=False)
    assert "signal_name0" not in outdbc.getvalue().decode('utf8')
    assert "signal_name1" not in outdbc.getvalue().decode('utf8')
    assert "signal_name" in outdbc.getvalue().decode('utf8')


def test_signal_inital_value():
    dbc = io.BytesIO(textwrap.dedent(u'''\
        BO_ 17 Frame_1: 8 Vector__XXX
        SG_ sig1 : 0|8@1- (1,0) [0|0] "" Vector__XXX


        BA_DEF_ SG_  "GenSigStartValue" FLOAT 0 100000000000;
        BA_ "GenSigStartValue" SG_ 17 sig1 2.7;
        
        SIG_VALTYPE_ 17 sig1 : 1;
    ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    assert matrix.frames[0].signal_by_name("sig1").initial_value == decimal.Decimal("2.7")
#    assert "GenSigStartValue" not in matrix.signal_defines

    outdbc = io.BytesIO()
    canmatrix.formats.dump(matrix, outdbc, "dbc")
    assert 'BA_ "GenSigStartValue" SG_ 17 sig1 2.7;' in outdbc.getvalue().decode('utf8')


def test_candbpp_startbit():
    dbc = io.BytesIO(textwrap.dedent(u'''\
        BO_ 1809 MSG: 8 DEV2
 SG_ SIG1 : 39|4@0+ (1,0) [0|1] ""  DEV1
 SG_ SIG2 : 52|1@0+ (1,0) [0|1] ""  DEV1
 SG_ SIG3 : 51|12@0+ (0.1,0) [0|360] "°"  DEV1
 SG_ SIG4 : 6|1@0+ (1,0) [0|1] ""  DEV1
 SG_ SIG5 : 5|1@0+ (1,0) [0|1] ""  DEV1
 SG_ SIG6 : 23|3@0+ (1,0) [0|1] ""  DEV1
 SG_ SIG7 : 7|1@0+ (1,0) [0|1] ""  DEV1
 SG_ SIG8 : 34|11@0+ (0.1,-102.4) [-32|32] "A"  DEV1
 SG_ SIG9 : 18|11@0+ (0.1,-102.4) [-62.5|62.5] "A"  DEV1
 SG_ SIG10 : 4|13@0+ (0.1,0) [350|450] "V"  DEV1
    ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    # Motorola forward LSB
    assert matrix.frames[0].signal_by_name("SIG1").get_startbit(True, True) == 36
    assert matrix.frames[0].signal_by_name("SIG2").get_startbit(True, True) == 52
    assert matrix.frames[0].signal_by_name("SIG3").get_startbit(True, True) == 56
    assert matrix.frames[0].signal_by_name("SIG4").get_startbit(True, True) == 6
    assert matrix.frames[0].signal_by_name("SIG5").get_startbit(True, True) == 5
    assert matrix.frames[0].signal_by_name("SIG6").get_startbit(True, True) == 21
    assert matrix.frames[0].signal_by_name("SIG7").get_startbit(True, True) == 7
    assert matrix.frames[0].signal_by_name("SIG8").get_startbit(True, True) == 40
    assert matrix.frames[0].signal_by_name("SIG9").get_startbit(True, True) == 24
    assert matrix.frames[0].signal_by_name("SIG10").get_startbit(True, True) == 8
    # Motorola forward MSB
    assert matrix.frames[0].signal_by_name("SIG1").get_startbit(True, False) == 39
    assert matrix.frames[0].signal_by_name("SIG2").get_startbit(True, False) == 52
    assert matrix.frames[0].signal_by_name("SIG3").get_startbit(True, False) == 51
    assert matrix.frames[0].signal_by_name("SIG4").get_startbit(True, False) == 6
    assert matrix.frames[0].signal_by_name("SIG5").get_startbit(True, False) == 5
    assert matrix.frames[0].signal_by_name("SIG6").get_startbit(True, False) == 23
    assert matrix.frames[0].signal_by_name("SIG7").get_startbit(True, False) == 7
    assert matrix.frames[0].signal_by_name("SIG8").get_startbit(True, False) == 34
    assert matrix.frames[0].signal_by_name("SIG9").get_startbit(True, False) == 18
    assert matrix.frames[0].signal_by_name("SIG10").get_startbit(True, False) == 4
    
    
def test_missing_space():
    dbc = io.BytesIO(textwrap.dedent(u'''\
        BO_ 17 Frame_1: 8 Vector__XXX
        SG_ sig1 : 0|8@1-(1,0)[0|0] "" Vector__XXX
        ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    assert matrix.frames[0].signals[0].name == "sig1"


def test_escaped_quotes():
    dbc = io.BytesIO(textwrap.dedent(r'''
        BO_ 17 Frame_1: 8 Vector__XXX
        SG_ Signal : 0|8@1-(1,0)[0|0] "" Vector__XXX
        
        VAL_ 17 Signal 0 "zero" 1 "one " 2 "string with \"escaped\" double quotes";
        ''').encode('utf-8'))
    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    assert matrix.frames[0].signals[0].values[2] == r'string with "escaped" double quotes'


def test_float_cycle_time():
    dbc = io.BytesIO(textwrap.dedent(u'''\
        BO_ 17 Frame_1: 8 Vector__XXX
        SG_ sig2 : 8|8@1- (1,0) [0|0] "" Vector__XXX
        SG_ sig1 : 0|8@1- (1,0) [0|0] "" Vector__XXX


        BA_DEF_ BO_  "GenMsgCycleTime" INT 10 3600000;
        BA_ "GenMsgCycleTime" BO_ 17 100.0;
''').encode('utf-8'))


    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")

    assert matrix.frames[0].cycle_time == 100


def test_without_ecu():
    dbc = io.BytesIO(textwrap.dedent(u'''\
            BO_ 17 Frame_1: 8 Vector__XXX
            SG_ A_B_C_D_E: 39|16@0+ (0.01,0) [0|655.35] "km/h"
    ''').encode('utf-8'))

    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    matrix.frames[0].signals[0].name == "A_B_C_D_E"


def test_default_initial_value():
    dbc = io.BytesIO(textwrap.dedent(u'''\
            BO_ 560 ECU1_Message: 1 ECU1
              SG_ ECU2_Signal : 0|8@0+ (1,-5) [-2|250] "g" ECU2

            BA_DEF_ SG_ "GenSigStartValue" FLOAT 0.0 100.0;
            
            BA_DEF_DEF_ "GenSigStartValue" 10.0;
    ''').encode('utf-8'))

    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    assert matrix.frames[0].signals[0].initial_value == 10

def test_keep_individual_inital_value():
    dbc = io.BytesIO(textwrap.dedent(u'''\
        BO_ 561 ECU1_Message2: 1 ECU1
            SG_ ECU2_Signal2 : 0|8@0+ (2,0) [-2|250] "g" ECU2

        BA_DEF_ SG_ "GenSigStartValue" FLOAT 0.0 100.0;
        
        BA_DEF_DEF_ "GenSigStartValue" 10.0;
        BA_ "GenSigStartValue" SG_ 561 ECU2_Signal2 42;
    ''').encode('utf-8'))
    matrix1 = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    assert matrix1.frames[0].signals[0].initial_value == decimal.Decimal('84')  # in matrix should be the physical value!
    outdbc = io.BytesIO()
    canmatrix.formats.dump(matrix1, outdbc, "dbc")
    # in dbc should be the raw value
    assert  'BA_ "GenSigStartValue" SG_ 561 ECU2_Signal2 42' in outdbc.getvalue().decode('utf8')


def test_individual_initial_value_merge():
    dbc1 = io.BytesIO(textwrap.dedent(u'''\
            BO_ 560 ECU1_Message: 1 ECU1
              SG_ ECU2_Signal : 0|8@0+ (1,-5) [-2|250] "g" ECU2

            BA_DEF_ SG_ "GenSigStartValue" FLOAT 0.0 100.0;
            
            BA_DEF_DEF_ "GenSigStartValue" 10.0;
    ''').encode('utf-8'))

    dbc2 = io.BytesIO(textwrap.dedent(u'''\
            BO_ 561 ECU1_Message2: 1 ECU1
              SG_ ECU2_Signal2 : 0|8@0+ (1,0) [-2|250] "g" ECU2

            BA_DEF_ SG_ "GenSigStartValue" FLOAT 0.0 100.0;
            
            BA_DEF_DEF_ "GenSigStartValue" 10.0;
            BA_ "GenSigStartValue" SG_ 561 ECU2_Signal2 42;
    ''').encode('utf-8'))

    matrix1 = canmatrix.formats.dbc.load(dbc1, dbcImportEncoding="utf8")
    matrix2 = canmatrix.formats.dbc.load(dbc2, dbcImportEncoding="utf8")
    matrix1.merge([matrix2])
    outdbc = io.BytesIO()
    canmatrix.formats.dump(matrix1, outdbc, "dbc")

    assert  'BA_ "GenSigStartValue" SG_ 561 ECU2_Signal2 42' in outdbc.getvalue().decode('utf8')

def test_no_initial_value():
    dbc = io.BytesIO(textwrap.dedent(u'''\
            BO_ 560 ECU1_Message: 1 ECU1
              SG_ ECU2_Signal : 0|8@0+ (1,-5) [-2|250] "g" ECU2

            BA_DEF_ SG_ "GenSigStartValue" FLOAT 0.0 100.0;
    ''').encode('utf-8'))

    matrix = canmatrix.formats.dbc.load(dbc, dbcImportEncoding="utf8")
    outdbc = io.BytesIO()
    canmatrix.formats.dump(matrix, outdbc, "dbc")
    assert 'BA_ "GenSigStartValue" SG_ 560 ECU2_Signal 5;' in outdbc.getvalue().decode('utf8')
#    assert matrix.frames[0].signals[0].initial_value == 10


def test_int_attribute_zero():
    db = CanMatrix()
    frame = Frame("some Frame")
    frame.add_signal(Signal("signal_name", size=1, start_bit=1))
    db.add_frame(frame)
    db.add_ecu_defines("ecu_define", "INT 0 10")
    db.add_ecu_defines("ecu_define2", "INT 0 10")
    db.add_ecu_defines("ecu_define3", "INT 0 10")

    db.add_frame_defines("test", "INT 0 10")
    db.add_frame_defines("test2", "INT 0 10")
    frame.add_attribute("test", 7)
    frame.add_attribute("test2", 0)

    ecu = Ecu('TestEcu')
    ecu.add_attribute('ecu_define', 1)
    ecu.add_attribute('ecu_define2', 0)

    db.add_ecu(ecu)

    outdbc = io.BytesIO()
    canmatrix.formats.dump(db, outdbc, "dbc")
    assert 'BO_ 0 7' in outdbc.getvalue().decode('utf8')
    assert 'BO_ 0 0' in outdbc.getvalue().decode('utf8')
    assert 'TestEcu 1' in outdbc.getvalue().decode('utf8')
    assert 'TestEcu 0' in outdbc.getvalue().decode('utf8')

def test_env_var_with_val():

    dbc = io.BytesIO(textwrap.dedent(u'''\
        EV_ XYZ2_RADAR__LDWwarningStatusFl11: 0 [1|3] "km/h" 2 1 DUMMY_NODE_VECTOR0 Vector__XXX;
        BA_ "SystemEnvVarLongSymbol" EV_ XYZ2_RADAR__LDWwarningStatusFl11 "XYZ2_RADAR__LDWwarningStatus__RADAR_XYZ2";
        VAL_ XYZ2_RADAR__LDWwarningStatusFl11 0 "on" 1 "off" 2 "reserved" 3 "error" ;
    ''').encode('utf-8'))

    matrix = canmatrix.formats.dbc.load(dbc)    
    key, var = next(iter(matrix.env_vars.items()))
    assert key == 'XYZ2_RADAR__LDWwarningStatus__RADAR_XYZ2'
    assert var['min'] == '1'
    assert var['max'] == '3'
    assert var['initialValue'] == '2'
    assert var['unit'] == 'km/h'
    assert len(var['values']) == 4
    assert var['values']['0'] == 'on'
    assert var['values']['1'] == 'off'

    